# Table of Contents

# Spring Data ElasticSearch

Spring Data ElasticSearch을 사용하면 더욱 추상화된 방법으로 Elastic Search에 접근할 수 있다.

# 의존성 설정

Spring Data ElasticSearch을 사용하려면 다음 의존성을 추가해야한다.

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-data-elasticsearch'
}

# 구성 클래스

AbstractElasticsearchConfiguration를 상속하는 구성 클래스를 다음과 같이 정의한다.

@Configuration
public class ElasticSearchConfig extends AbstractElasticsearchConfiguration {

    @Override
    public RestHighLevelClient elasticsearchClient() {
        final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                .connectedTo("localhost:9200")
                .build();
        return RestClients.create(clientConfiguration).rest();
    }
}

이 클래스의 elasticsearchClient() 메소드는 RestHighLevelClient 타입의 객체를 반환한다. AbstractElasticsearchConfiguration 정의를 보면 이 메소드의 반환 객체를 사용하여 ElasticsearchRestTemplate 객체를 빈으로 등록한다.

public abstract class AbstractElasticsearchConfiguration extends ElasticsearchConfigurationSupport {

	@Bean
	public abstract RestHighLevelClient elasticsearchClient();

	@Bean(name = { "elasticsearchOperations", "elasticsearchTemplate" })
	public ElasticsearchOperations elasticsearchOperations(ElasticsearchConverter elasticsearchConverter,
			RestHighLevelClient elasticsearchClient) {

		ElasticsearchRestTemplate template = new ElasticsearchRestTemplate(elasticsearchClient, elasticsearchConverter);
		template.setRefreshPolicy(refreshPolicy());

		return template;
	}
}

ElasticsearchRestTemplate클래스는 ElasticsearchOperations인터페이스의 구현체다. 따라서 다음과 같이 ElasticsearchOperations을 주입받아 사용할 수 있다.

@SpringBootTest
class Test {

    @Autowired
    private ElasticsearchOperations operations;

	// ...

# 데이터 모델 정의

import lombok.Builder;
import lombok.Getter;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;

@Document(indexName = "player")
@Getter
@Builder
public class PlayerDocument {
    @Id
    private String id;

    @Field(type = FieldType.Text)
    private String name;

    @Field(type = FieldType.Text)
    private String nation;

    @Field(type = FieldType.Integer)
    private int age;

    @Field(type = FieldType.Integer)
    private int score;

    @Field(type = FieldType.Integer)
    private int assist;
}

# ElasticsearchOperations

ElasticsearchOperations클래스는 CRUD 연산을 요청하기 위한 다양한 메소드를 제공한다.

@SpringBootTest
class Test {

    @Autowired
    private ElasticsearchOperations operations;

    @Test
    void test1() {
        PlayerDocument player = PlayerDocument.builder()
                .name("neymar")
                .nation("brazil")
                .age(31)
                .score(34)
                .assist(40)
                .build();

        operations.save(player);
    }

실제로 저장되는 도큐먼트는 다음과 같다.


















 
 
 
 
 
 
 
 
 
 
 
 
 
 




$ curl -XGET http://localhost:9200/player/_search\?pretty
{
  "took" : 0,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "player",
        "_type" : "_doc",
        "_id" : "OyG-PYEBhCK0-IfWnSy8",
        "_score" : 1.0,
        "_source" : {
          "_class" : "com.yologger.samples.project.repository.PlayerDocument",
          "name" : "neymar",
          "nation" : "brazil",
          "age" : 31,
          "score" : 34,
          "assist" : 40
        }
      }
    ]
  }
}

# ElasticsearchRepository

ElasticsearchRepository 인터페이스를 사용하면 더욱 추상화된 방법으로 CRUD 연산을 할 수 있다. 이 인터페이스를 사용하려면 먼저 구성 클래스에 @EnableElasticsearchRepositories 어노테이션을 추가해야한다.

 


 











import org.springframework.data.elasticsearch.repository.config.EnableElasticsearchRepositories;

@Configuration
@EnableElasticsearchRepositories
public class ElasticSearchConfig extends AbstractElasticsearchConfiguration {

    @Override
    public RestHighLevelClient elasticsearchClient() {
        final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
                .connectedTo("localhost:9200")
                .build();
        return RestClients.create(clientConfiguration).rest();
    }
}

그 다음 ElasticsearchRepository인터페이스를 구현하는 클래스를 정의한다.

public interface PlayerRepository extends ElasticsearchRepository<PlayerDocument, String> {
}

이제 필요한 곳에서 다음과 같이 구현체를 주입받아 사용할 수 있다.

@SpringBootTest
class Test {

    @Autowired
    private PlayerRepository playerRepository;

    @Test
    void test1() {
        PlayerDocument player = PlayerDocument.builder()
                .name("figo")
                .nation("portugal")
                .age(45)
                .score(34)
                .assist(40)
                .build();

        PlayerDocument saved = playerRepository.save(player);

        assertThat(saved.getName()).isEqualTo("figo");
    }
}

# Custom Repository

더 복잡한 쿼리가 필요한 경우 다음과 같이 Custom Repository를 사용할 수도 있다.

public interface CustomPlayerRepository {
    List<PlayerDocument> searchByNation(String name);
}
@RequiredArgsConstructor
@Component
public class CustomPlayerRepositoryImpl implements CustomPlayerRepository {

    private final ElasticsearchOperations elasticsearchOperations;

    @Override
    public List<PlayerDocument> searchByNation(String name) {
        // 직접 구현 ...
    }
}
public interface PlayerRepository extends ElasticsearchRepository<PlayerDocument, String>, CustomPlayerRepository {
}